﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DDD4Fun.Mediator
{
    public sealed class MessageMediator : IMessageMediator
    {
       #region 定义一个包含接收者信息的类型
        private class WeakActionAndToken
        {
            public object Token { get; set; }
            public WeakAction Action { get; set; }
        }
       #endregion

        private static readonly Lazy<IMessageMediator> _instance
            = new Lazy<IMessageMediator>(() => new MessageMediator(), true);

        public static IMessageMediator Instance
        {
            get
            {
                return _instance.Value;
            }
        }

        private readonly object _registerLock = new object();
        private Dictionary<Type, List<WeakActionAndToken>> _recipientsStrictAction;
        
        #region IMessageMediator Members

        public void Register<TMessage>(object recipient, Action<TMessage> action)
        {
            Register<TMessage>(recipient, action, null);
        }

        public void Register<TMessage>(object recipient, Action<TMessage> action,object token)
        {
            lock (this._registerLock)
            {
                Type typeFromHandle = typeof(TMessage);
                Dictionary<Type, List<WeakActionAndToken>> dictionary;
                if (_recipientsStrictAction == null)
                {
                    _recipientsStrictAction = new Dictionary<Type, List<WeakActionAndToken>>();
                }
                dictionary = _recipientsStrictAction;

                List<WeakActionAndToken> list;
                if (!dictionary.ContainsKey(typeFromHandle))
                {
                    list = new List<WeakActionAndToken>();
                    dictionary.Add(typeFromHandle, list);
                }
                else
                {
                    list = dictionary[typeFromHandle];
                }

                WeakAction<TMessage> action2 = new WeakAction<TMessage>(recipient, action);
                WeakActionAndToken weakActionAndToken = new WeakActionAndToken
                {
                    Action = action2,
                    Token = token
                };
                list.Add(weakActionAndToken);
            }
        }

        public void Send<TMessage>(TMessage message)
        {
            SendToList(message, null,null);
        }

        public void Send<TMessage, TTarget>(TMessage message)
        {
            SendToList(message, typeof(TTarget),null);
        }

        public void Send<TMessage>(TMessage message, object token)
        {
            SendToList(message, null, token);
        }

        private void SendToList<TMessage>(TMessage message, Type messageTargetType, object token)
        {
            Type type = message.GetType();
            if (_recipientsStrictAction != null && _recipientsStrictAction.ContainsKey(type))
            {
                List<WeakActionAndToken> list = _recipientsStrictAction[type];
                //do send
                if (list != null)
                {
                    foreach (var current in list)
                    {
                        IExecuteWithObject executeWithObject = current.Action as IExecuteWithObject;
                        if (executeWithObject != null && current.Action.IsAlive
                            && current.Action.Target != null
                            && (messageTargetType == null
                            || current.Action.Target.GetType() == messageTargetType)
                            && (
                            (token == null) || token.Equals(current.Token))
                            )
                        {
                            executeWithObject.ExecuteWithObject(message);
                        }
                    }
                }
            }
        }

        public void UnRegister(object recipient)
        {
            UnRegisterList(recipient);
        }

        public void UnRegister<TMessage>(object recipient)
        {
            UnRegisterList<TMessage>(recipient, null);
        }

        public void UnRegister<TMessage>(object recipient, object token)
        {
            UnRegisterList<TMessage>(recipient, token);
        }

        public void UnRegisterList(object recipient)
        {
            var lists = _recipientsStrictAction;
            if (recipient == null || lists == null || lists.Count() == 0 )
            {
                return;
            }

            foreach (var currentlist in lists)
            {
                foreach (var current in currentlist.Value)
                {
                    WeakAction weakAction = current.Action;
                    if (weakAction != null
                        && recipient == weakAction.Target)
                    {
                        current.Action.MarkForDeletion();
                    }
                }
            }
        }

        public void UnRegisterList<TMessage>(object recipient, object token)
        {
            Type typeFromHandle = typeof(TMessage);
            var lists = _recipientsStrictAction;
            if (recipient == null || lists == null || lists.Count() == 0 || !lists.ContainsKey(typeFromHandle))
            {
                return;
            }

            foreach (var current in lists[typeFromHandle])
            {
                WeakAction<TMessage> weakAction = current.Action as WeakAction<TMessage>;
                if (weakAction != null 
                    && recipient == weakAction.Target 
                    && (token == null || token.Equals(current.Token)))
                {
                    current.Action.MarkForDeletion();
                }
            }
        }

        #endregion
    }
}
